

#include "stdafx.h"
#include "pf_dimangular.h"

#include "pf_constructionline.h"
#include "pf_filterdxf.h"
#include "pf_graphic.h"
#include "pf_information.h"
#include "pf_solid.h"
#include "pf_text.h"


/**
 * Constructor.
 *
 * @para parent Parent Entity Container.
 * @para d Common dimension geometrical data.
 * @para ed Extended geometrical data for angular dimension.
 */
PF_DimAngular::PF_DimAngular(PF_Container* parent,
                             const PF_DimensionAttribute& d,
                             const PF_DimAngularAttribute& ed)
        : PF_Dimension(parent, d), edata(ed) {

    calculateBorders();
}



/**
 * @return Automatically created label for the default 
 * measurement of this dimension.
 */
QString PF_DimAngular::getMeasuredLabel() {
    QString ret;

	int dimaunit = getGraphicVariableInt("$DIMAUNIT", 0);
	int dimadec = getGraphicVariableInt("$DIMADEC", 0);

	ret = PF_Units::formatAngle(getAngle(), 
		PF_FilterDXF::numberToAngleFormat(dimaunit),
		dimadec);
	
    /*
        ret = QString("%1%2")
          .arg(PF_Math::rad2deg(getAngle()))
          .arg(QChar(0xB0));
	*/
    return ret;
}



/**
 * @return Angle of the measured dimension.
 */
double PF_DimAngular::getAngle() {
    double ang1 = 0.0;
    double ang2 = 0.0;
    bool reversed = false;
	PF_Vector p1;
	PF_Vector p2;

    getAngles(ang1, ang2, reversed, p1, p2);

    if (!reversed) {
        if (ang2<ang1) {
            ang2+=2*M_PI;
        }
        return ang2-ang1;
    } else {
        if (ang1<ang2) {
            ang1+=2*M_PI;
        }
        return ang1-ang2;
    }
}



/**
 * @return Center of the measured dimension.
 */
PF_Vector PF_DimAngular::getCenter() {
    PF_ConstructionLine l1(NULL, PF_ConstructionLineAttribute(edata.definitionPoint1,
                           edata.definitionPoint2));
    PF_ConstructionLine l2(NULL, PF_ConstructionLineAttribute(edata.definitionPoint3,
                           data.definitionPoint));
    PF_VectorSolutions vs = PF_Information::getIntersection(&l1, &l2, false);

    return vs.get(0);
}



/**
 * finds out which angles this dimension actually measures.
 *
 * @param ang1 Reference will return the start angle
 * @param ang2 Reference will return the end angle
 * @param reversed Reference will return the reversed flag.
 *
 * @return true: on success
 */
bool PF_DimAngular::getAngles(double& ang1, double& ang2, bool& reversed,
                              PF_Vector& p1, PF_Vector& p2) {

    PF_Vector center = getCenter();
    double ang = center.angleTo(edata.definitionPoint4);
    bool done = false;

    // find out the angles this dimension refers to:
    for (int f1=0; f1<=1 && !done; ++f1) {
        ang1 = PF_Math::correctAngle(
                   edata.definitionPoint1.angleTo(edata.definitionPoint2) + f1*M_PI);
        if (f1==0) {
            p1 = edata.definitionPoint2;
        } else {
            p1 = edata.definitionPoint1;
        }
        for (int f2=0; f2<=1 && !done; ++f2) {
            ang2 = PF_Math::correctAngle(
                       edata.definitionPoint3.angleTo(data.definitionPoint) + f2*M_PI);
            if (f2==0) {
                p2 = data.definitionPoint;
            } else {
                p2 = edata.definitionPoint3;
            }
            for (int t=0; t<=1 && !done; ++t) {
                reversed = (bool)t;

                double angDiff;

                if (!reversed) {
                    if (ang2<ang1) {
                        ang2+=2*M_PI;
                    }
                    angDiff = ang2-ang1;
                } else {
                    if (ang1<ang2) {
                        ang1+=2*M_PI;
                    }
                    angDiff = ang1-ang2;
                }

                ang1 = PF_Math::correctAngle(ang1);
                ang2 = PF_Math::correctAngle(ang2);

                if (PF_Math::isAngleBetween(ang, ang1, ang2, reversed) &&
                        angDiff<=M_PI) {
                    done = true;
                    break;
                }
            }
        }
    }

    return done;
}



/**
 * Updates the sub entities of this dimension. Called when the 
 * dimension or the position, alignment, .. changes.
 *
 * @param autoText Automatically reposition the text label
 */
void PF_DimAngular::update(bool /*autoText*/) {

    PF_DEBUG->print("PF_DimAngular::update");

    clear();
	
	if (isUndone()) {
		return;
	}

    // distance from entities (DIMEXO)
    double dimexo = getExtensionLineOffset();
    // extension line extension (DIMEXE)
    double dimexe = getExtensionLineExtension();
    // text height (DIMTXT)
    double dimtxt = getTextHeight();
    // text distance to line (DIMGAP)
    double dimgap = getDimensionLineGap();

    // find out center:
    PF_Vector center = getCenter();

    if (!center.valid) {
        return;
    }

    double ang1 = 0.0;
    double ang2 = 0.0;
    bool reversed = false;
    PF_Vector p1;
    PF_Vector p2;

    getAngles(ang1, ang2, reversed, p1, p2);

    double rad = edata.definitionPoint4.distanceTo(center);

    PF_Line* line;
    PF_Vector dir;
    double len;
    double dist;

    // 1st extension line:
    dist = center.distanceTo(p1);
    len = rad - dist + dimexe;
    dir.setPolar(1.0, ang1);
    line = new PF_Line(this,
                       PF_LineAttribute(center + dir*dist + dir*dimexo,
                                   center + dir*dist + dir*len));
    line->setPen(PF_Pen(LX_Define::FlagInvalid));
    line->setLayer(NULL);
    addEntity(line);

    // 2nd extension line:
    dist = center.distanceTo(p2);
    len = rad - dist + dimexe;
    dir.setPolar(1.0, ang2);
    line = new PF_Line(this,
                       PF_LineAttribute(center + dir*dist + dir*dimexo,
                                   center + dir*dist + dir*len));
    line->setPen(PF_Pen(LX_Define::FlagInvalid));
    line->setLayer(NULL);
    addEntity(line);

    // Create dimension line (arc):
    PF_Arc* arc = new PF_Arc(this,
                             PF_ArcAttribute(center,
                                        rad, ang1, ang2, reversed));
    arc->setPen(PF_Pen(LX_Define::FlagInvalid));
    arc->setLayer(NULL);
    addEntity(arc);

    // length of dimension arc:
    double distance = arc->getLength();

    // do we have to put the arrows outside of the arc?
    bool outsideArrows = (distance<getArrowSize()*2);

    // arrow angles:
    double arrowAngle1, arrowAngle2;
    double arrowAng;
	if (rad>1.0e-6) {
		arrowAng = getArrowSize() / rad;
	}
	else {
		arrowAng = 0.0;
	}
    PF_Vector v1, v2;
    if (!arc->isReversed()) {
        v1.setPolar(rad, arc->getAngle1()+arrowAng);
    } else {
        v1.setPolar(rad, arc->getAngle1()-arrowAng);
    }
    v1+=arc->getCenter();
    arrowAngle1 = arc->getStartpoint().angleTo(v1);


    if (!arc->isReversed()) {
        v2.setPolar(rad, arc->getAngle2()-arrowAng);
    } else {
        v2.setPolar(rad, arc->getAngle2()+arrowAng);
    }
    v2+=arc->getCenter();
    arrowAngle2 = arc->getEndpoint().angleTo(v2);

    if (!outsideArrows) {
        arrowAngle1 = arrowAngle1+M_PI;
        arrowAngle2 = arrowAngle2+M_PI;
    }

    // Arrows:
    PF_SolidAttribute sd;
    PF_Solid* arrow;

    // arrow 1
    arrow = new PF_Solid(this, sd);
    arrow->shapeArrow(arc->getStartpoint(),
                      arrowAngle1,
                      getArrowSize());
    arrow->setPen(PF_Pen(LX_Define::FlagInvalid));
    arrow->setLayer(NULL);
    addEntity(arrow);

    // arrow 2:
    arrow = new PF_Solid(this, sd);
    arrow->shapeArrow(arc->getEndpoint(),
                      arrowAngle2,
                      getArrowSize());
    arrow->setPen(PF_Pen(LX_Define::FlagInvalid));
    arrow->setLayer(NULL);
    addEntity(arrow);


    // text label:
    PF_TextAttribute textData;
    PF_Vector textPos = arc->getMiddlepoint();

    PF_Vector distV;
    double textAngle;
    double dimAngle1 = textPos.angleTo(arc->getCenter())-M_PI/2.0;

    // rotate text so it's readable from the bottom or right (ISO)
    // quadrant 1 & 4
    if (dimAngle1>M_PI/2.0*3.0+0.001 ||
            dimAngle1<M_PI/2.0+0.001) {

        distV.setPolar(dimgap, dimAngle1+M_PI/2.0);
        textAngle = dimAngle1;
    }
    // quadrant 2 & 3
    else {
        distV.setPolar(dimgap, dimAngle1-M_PI/2.0);
        textAngle = dimAngle1+M_PI;
    }

    // move text away from dimension line:
    textPos+=distV;

    textData = PF_TextAttribute(textPos,
                           dimtxt, 30.0,
                           LX_Define::VAlignBottom,
                           LX_Define::HAlignCenter,
                           LX_Define::LeftToRight,
                           LX_Define::Exact,
                           1.0,
                           getLabel(),
                           "standard",
                           textAngle);

    PF_Text* text = new PF_Text(this, textData);

    // move text to the side:
    text->setPen(PF_Pen(LX_Define::FlagInvalid));
    text->setLayer(NULL);
    addEntity(text);

    calculateBorders();
}



void PF_DimAngular::move(PF_Vector offset) {
    PF_Dimension::move(offset);

    edata.definitionPoint1.move(offset);
    edata.definitionPoint2.move(offset);
    edata.definitionPoint3.move(offset);
    edata.definitionPoint4.move(offset);
    update();
}



void PF_DimAngular::rotate(PF_Vector center, double angle) {
    PF_Dimension::rotate(center, angle);

    edata.definitionPoint1.rotate(center, angle);
    edata.definitionPoint2.rotate(center, angle);
    edata.definitionPoint3.rotate(center, angle);
    edata.definitionPoint4.rotate(center, angle);
    update();
}



void PF_DimAngular::scale(PF_Vector center, PF_Vector factor) {
    PF_Dimension::scale(center, factor);

    edata.definitionPoint1.scale(center, factor);
    edata.definitionPoint2.scale(center, factor);
    edata.definitionPoint3.scale(center, factor);
    edata.definitionPoint4.scale(center, factor);
    update();
}



void PF_DimAngular::mirror(PF_Vector axisPoint1, PF_Vector axisPoint2) {
    PF_Dimension::mirror(axisPoint1, axisPoint2);

    edata.definitionPoint1.mirror(axisPoint1, axisPoint2);
    edata.definitionPoint2.mirror(axisPoint1, axisPoint2);
    edata.definitionPoint3.mirror(axisPoint1, axisPoint2);
    edata.definitionPoint4.mirror(axisPoint1, axisPoint2);
    update();
}

/**
 * Dumps the point's data to stdout.
 */
std::ostream& operator << (std::ostream& os, const PF_DimAngular& d) {
    os << " DimAngular: " << d.getData() << "\n" << d.getEData() << "\n";
    return os;
}

